#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "drv_common_def.h"
#include "drv_serial_port.h"
#include "dev_common.h"
#include "dev_pin.h"
typedef enum {
    DEV_UART_MODE_RX = 0,
    DEV_UART_MODE_TX,
    DEV_UART_MODE_TX_RX
} Dev_UartMode;

typedef struct {
    USART_TypeDef *uartInstance;
    PhyPin txPin;
    PhyPin rxPin;
    uint32_t baudRate;
    Dev_UartMode mode;
} Dev_UartInitConfig;

typedef struct Dev_SerialPort_t Dev_SerialPort;
struct Dev_SerialPort_t {
    SerialPort base;
    UART_HandleTypeDef huart;
    DMA_HandleTypeDef hdma;
    SerialReadCallBack func;
    uint8_t buffer[100];
};

int _write(int fd, char *ptr, int len)
{
    SerialPort *port = GetDebugSerialPort();
    assert(port != NULL);
    port->write(port, (const uint8_t *)ptr, len);
    return len;
}

void _DeleteSerialPort(SerialPort *pthis)
{
    if (pthis == NULL) {
        return;
    }
    free(pthis);
}

errno_t _Dev_UartWrite(SerialPort *pthis, const uint8_t *buf, size_t len)
{
    Dev_SerialPort *me = (Dev_SerialPort *)pthis;
    return HAL_UART_Transmit(&me->huart, (uint8_t *)buf, len, 0xffff);
}

errno_t _Dev_SetUartCallBack(SerialPort *pthis, SerialReadCallBack func)
{
    Dev_SerialPort *me = (Dev_SerialPort *)pthis;
    me->func = func;
    return STATE_OK;
}

SerialPort *DebugSerialPortCtor(const Dev_UartInitConfig *config)
{
    assert(config != NULL);
    Dev_SerialPort *me = (Dev_SerialPort *)malloc(sizeof(Dev_SerialPort));

    me->base.read = NULL;
    me->base.write = _Dev_UartWrite;
    me->base.setReadCallBack = _Dev_SetUartCallBack;
    me->base.remove = _DeleteSerialPort;

    EnableDevCLK(config->txPin.gpio);
    EnableDevCLK(config->rxPin.gpio);
    EnableDevCLK(USART1);

    GPIO_InitTypeDef GPIO_InitStucture;
    GPIO_InitStucture.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStucture.Pin = config->txPin.pinId;
    GPIO_InitStucture.Pull = GPIO_PULLUP;
    GPIO_InitStucture.Speed = GPIO_SPEED_FREQ_HIGH;
#ifdef STM32F401xC
    GPIO_InitStucture.Alternate = GPIO_AF7_USART1;
#endif
    HAL_GPIO_Init(config->txPin.gpio, &GPIO_InitStucture);

    GPIO_InitStucture.Pin = config->rxPin.pinId;
#ifndef STM32F401xC
    GPIO_InitStucture.Mode = GPIO_MODE_AF_INPUT;
#endif
    HAL_GPIO_Init(config->rxPin.gpio, &GPIO_InitStucture);

    me->huart.Instance = config->uartInstance;
    me->huart.Init.BaudRate = config->baudRate;
    me->huart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    me->huart.Init.Mode = config->mode;
    me->huart.Init.OverSampling = UART_OVERSAMPLING_16;
    me->huart.Init.Parity = UART_PARITY_NONE;
    me->huart.Init.StopBits = UART_STOPBITS_1;
    me->huart.Init.WordLength = UART_WORDLENGTH_8B;
    HAL_UART_Init(&me->huart);
#ifdef STM32F401xC
    EnableDevCLK(DMA2);
    me->hdma.Instance = DMA2_Stream2;
    me->hdma.Init.Channel = DMA_CHANNEL_4;
    me->hdma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
#else
    EnableDevCLK(DMA1);
    me->hdma.Instance = DMA1_Channel5;
#endif
    me->hdma.Init.Mode = DMA_NORMAL;
    me->hdma.Init.Direction = DMA_PERIPH_TO_MEMORY;
    me->hdma.Init.PeriphInc = DMA_PINC_DISABLE;
    me->hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    me->hdma.Init.MemInc = DMA_MINC_ENABLE;
    me->hdma.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    me->hdma.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    HAL_DMA_DeInit(&me->hdma);
    HAL_DMA_Init(&me->hdma);
    __HAL_LINKDMA(&me->huart, hdmarx, me->hdma);
    // 中断
    HAL_NVIC_SetPriority(USART1_IRQn, 13, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
    HAL_UART_Receive_DMA(&me->huart, me->buffer, 100);
    __HAL_UART_ENABLE_IT(&me->huart, UART_IT_IDLE);
    return (SerialPort *)me;
}

void USART1_IRQHandler()
{
    Dev_SerialPort *debugUart = (Dev_SerialPort *)GetDebugSerialPort();
    if (__HAL_UART_GET_FLAG(&debugUart->huart, UART_FLAG_IDLE) != RESET) {
        uint32_t len = 100 - __HAL_DMA_GET_COUNTER(&debugUart->hdma);
        debugUart->buffer[len] = 0;
        debugUart->func((const char *)debugUart->buffer, strlen(debugUart->buffer));
        __HAL_UART_CLEAR_IDLEFLAG(&debugUart->huart);
        HAL_UART_DMAStop(&debugUart->huart);
        HAL_UART_Receive_DMA(&debugUart->huart, debugUart->buffer, 100);
    }
}

const Dev_UartInitConfig g_debugUartConfig = {
    .uartInstance = USART1,
    .rxPin = {GPIOA, GPIO_PIN_10},
    .txPin = {GPIOA, GPIO_PIN_9},
    .baudRate = 115200,
    .mode = UART_MODE_TX_RX
};

void Dev_Debug_UartInit(void)
{
    SerialPort *debugUart = DebugSerialPortCtor(&g_debugUartConfig);
    SetDebugSerialPort(debugUart);
    SetDebugSerialPortCallback(DebugUartCallBack);
}
